/**
 * Copyright 2020 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.dbfly.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * Bean工具类
 * 
 * @author jianggujin
 *
 */
public class JBeanUtils {
    /**
     * 获得指定对象指定属性的值
     * 
     * @param <T>
     * @param obj
     * @param name
     * @return
     */
    public static <T> T getValue(Object obj, String name) {
        return getValue(obj, obj.getClass(), name);
    }

    /**
     * 获得指定对象指定属性的值
     * 
     * @param <T>
     * @param obj
     * @param clazz 从指定的class开始查找
     * @param name
     * @return
     */
    public static <T> T getValue(Object obj, Class<?> clazz, String name) {
        return getValue(obj, findField(clazz, name));
    }

    /**
     * 获得指定对象指定属性的值
     * 
     * @param <T>
     * @param obj
     * @param field
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getValue(Object obj, Field field) {
        boolean accessible = field.isAccessible();
        try {
            field.setAccessible(true);
            return (T) field.get(obj);
        } catch (Exception e) {
            throw JExceptionUtils.handleException(e);
        } finally {
            field.setAccessible(accessible);
        }
    }

    /**
     * 设置指定对象指定属性的值
     * 
     * @param obj
     * @param name
     * @param value
     */
    public static void setValue(Object obj, String name, Object value) {
        setValue(obj, obj.getClass(), name, value);
    }

    /**
     * 指定对象指定属性的值
     * 
     * @param obj
     * @param clazz 从指定的class开始查找
     * @param name
     * @param value
     */
    public static void setValue(Object obj, Class<?> clazz, String name, Object value) {
        setValue(obj, findField(clazz, name), value);
    }

    /**
     * 指定对象指定属性的值
     * 
     * @param obj
     * @param field
     * @param value
     */
    public static void setValue(Object obj, Field field, Object value) {
        boolean accessible = field.isAccessible();
        try {
            field.setAccessible(true);
            field.set(obj, value);
        } catch (Exception e) {
            throw JExceptionUtils.handleException(e);
        } finally {
            field.setAccessible(accessible);
        }
    }

    /**
     * 执行指定对象的指定无参方法
     * 
     * @param obj
     * @param name
     * @return
     */
    public static Object invokeMethod(Object obj, String name) {
        return invokeMethod(obj, obj.getClass(), name);
    }

    /**
     * 执行指定对象的指定无参方法
     * 
     * @param obj
     * @param clazz 从指定的class开始查找
     * @param name
     * @return
     */
    public static Object invokeMethod(Object obj, Class<?> clazz, String name) {
        return invokeMethod(obj, findMethod(clazz, name));
    }

    /**
     * 执行指定对象的指定方法
     * 
     * @param obj
     * @param method
     * @param parameters
     * @return
     */
    public static Object invokeMethod(Object obj, Method method, Object... parameters) {
        boolean accessible = method.isAccessible();
        try {
            method.setAccessible(true);
            return method.invoke(obj, parameters);
        } catch (Exception e) {
            throw JExceptionUtils.handleException(e);
        } finally {
            method.setAccessible(accessible);
        }
    }

    /**
     * 查找指定名称Field
     * 
     * @param clazz
     * @param name
     * @return
     */
    public static Field findField(Class<?> clazz, String name) {
        Field field = null;
        while (clazz != null && !Object.class.equals(clazz)) {
            try {
                field = clazz.getDeclaredField(name);
                break;
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
            }
        }
        return field;
    }

    /**
     * 查找Field
     * 
     * @param clazz
     * @param predicate
     * @return
     */
    public static List<Field> findFields(Class<?> clazz, Predicate<Field> predicate) {
        return filter(clazz, Class::getDeclaredFields, predicate);
    }

    /**
     * 查找指定名称与参数类型的方法
     * 
     * @param clazz
     * @param name
     * @param parameterTypes
     * @return
     */
    public static Method findMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
        Method method = null;
        while (clazz != null && !Object.class.equals(clazz)) {
            try {
                method = clazz.getDeclaredMethod(name, parameterTypes);
                break;
            } catch (NoSuchMethodException e) {
                clazz = clazz.getSuperclass();
            }
        }
        return method;
    }

    /**
     * 查找Method
     * 
     * @param clazz
     * @param predicate
     * @return
     */
    public static List<Method> findMethods(Class<?> clazz, Predicate<Method> predicate) {
        return filter(clazz, Class::getDeclaredMethods, predicate);
    }

    /**
     * 实例化指定类对象
     * 
     * @param <T>
     * @param clazz
     * @return
     */
    public static <T> T newInstance(Class<T> clazz) {
        try {
            return (T) clazz.newInstance();
        } catch (Exception e) {
            throw JExceptionUtils.handleException(e);
        }
    }

    /**
     * 获得指定类型的构造方法
     * 
     * @param <T>
     * @param clazz
     * @param parameterTypes
     * @return
     */
    public static <T> Constructor<T> getConstructor(Class<T> clazz, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredConstructor(parameterTypes);
        } catch (Exception e) {
            throw JExceptionUtils.handleException(e);
        }
    }

    /**
     * 实例化指定类对象
     * 
     * @param <T>
     * @param constructor
     * @param parameters
     * @return
     */
    public static <T> T newInstance(Constructor<T> constructor, Object... parameters) {
        boolean accessible = constructor.isAccessible();
        try {
            constructor.setAccessible(true);
            return constructor.newInstance(parameters);
        } catch (Exception e) {
            throw JExceptionUtils.handleException(e);
        } finally {
            constructor.setAccessible(accessible);
        }
    }

    /**
     * 在类中查找指定内容
     * 
     * @param <T>
     * @param clazz
     * @param function
     * @param predicate
     * @return
     */
    public static <T> List<T> filter(Class<?> clazz, Function<Class<?>, T[]> function, Predicate<T> predicate) {
        List<T> filters = new ArrayList<>();
        int dept = 0;
        int index = 0;
        while (clazz != null && !Object.class.equals(clazz)) {
            T[] items = function.apply(clazz);
            if (items != null) {
                for (T item : items) {
                    if (predicate == null || predicate.test(item)) {
                        if (dept != 0) {
                            filters.add(index++, item);
                        } else {
                            filters.add(item);
                        }
                    }
                }
            }
            dept++;
            index = 0;
            clazz = clazz.getSuperclass();
        }
        return filters;
    }
}
